home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / fw_xemacs.idb / usr / freeware / lib / xemacs-20.4 / lisp / modes / ksh-mode.el.z / ksh-mode.el
Encoding:
Text File  |  1998-05-21  |  40.1 KB  |  1,308 lines

  1. ;; ksh-mode.el --- sh (ksh, bash) script editing mode for GNU Emacs.
  2.  
  3. ;; Copyright (C) 1992-96 Gary Ellison.
  4.  
  5. ;; This file is part of XEmacs.
  6.  
  7. ;; XEmacs is free software; you can redistribute it and/or modify it
  8. ;; under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation; either version 2, or (at your option)
  10. ;; any later version.
  11.  
  12. ;; XEmacs is distributed in the hope that it will be useful, but
  13. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15. ;; General Public License for more details.
  16.  
  17. ;; You should have received a copy of the GNU General Public License
  18. ;; along with XEmacs; see the file COPYING.  If not, write to the 
  19. ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  20. ;; Boston, MA 02111-1307, USA.
  21.  
  22. ;; $Source: /home/gfe/archive/src/RCS/ksh-mode.el,v $ --  
  23. ;;
  24. ;; LCD Archive Entry:
  25. ;; ksh-mode|Gary F. Ellison|Gary.F.Ellison@ATT.COM
  26. ;; |Mode for editing sh/ksh/bash scripts
  27. ;; |$Date: 1996/08/02 22:32:16 $|$Revision: 2.9 $|~/modes/ksh-mode.el.Z|
  28.  
  29. ;; Author: Gary F. Ellison <Gary.F.Ellison@ATT.COM>
  30. ;;                   AT&T  Laboratories
  31. ;;                   6200 East Broad Street
  32. ;;                   Columbus, Ohio 43213 USA
  33. ;;
  34. ;; Maintainer: Gary F. Ellison <Gary.F.Ellison@ATT.COM>
  35. ;; Created: Fri Jun 19
  36. ;; $Revision: 2.9 $
  37. ;; Keywords: shell, korn, bourne, sh, ksh, bash
  38. ;;
  39. ;; Delta On   $Date: 1996/08/02 22:32:16 $
  40. ;; Last Modified By: Gary Ellison
  41. ;; Last Modified On: Mon Sep 11 12:26:47 1995
  42. ;; Update Count    : 35
  43. ;; Status          : Highly Functional
  44. ;;
  45.  
  46. ;;; Commentary:
  47.  
  48. ;;
  49. ;; Description:
  50. ;;   sh, ksh, and bash script editing commands for emacs.
  51. ;; 
  52. ;; Installation:
  53. ;;   Put ksh-mode.el in some directory in your load-path.
  54. ;;   Refer to the installation section of ksh-mode's function definition.
  55. ;;
  56. ;; Usage:
  57. ;;   This major mode assists shell script writers with indentation
  58. ;;   control and control structure construct matching in much the same
  59. ;;   fashion as other programming language modes. Invoke describe-mode
  60. ;;   for more information.
  61. ;; 
  62. ;; Bugs:
  63. ;;   When the ksh-align-to-keyword is non-nil and the nester
  64. ;;   is a multi-command expression with a compound command
  65. ;;   the lines following the compound end will align incorrectly
  66. ;;   to the compound command instead of it's current indentation.
  67. ;;   The fix will probably require the detection of syntax elements
  68. ;;   in the nesting line.
  69. ;;   
  70. ;;   Function ending brace "}" must be on a separate line for indent-line
  71. ;;   to do the right thing.
  72. ;;
  73. ;;   Explicit function definition matching will proclaim in the minibuffer
  74. ;;   "No matching compound command" followed by "Matched ... "
  75. ;;
  76. ;;   indent-for-comment fails to recognize a comment starting in column 0,
  77. ;;   hence it moves the comment-start in comment-column.
  78.  
  79. ;;; Code:
  80.  
  81. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  82. ;;
  83. ;; HISTORY 
  84. ;; 6-Apr-96             Gary Ellison <gary.f.ellison@att.com>
  85. ;;    Depreciated font-lock-doc-string-face.
  86. ;;    Narly keywords inside strings bug fixed. 
  87. ;;
  88. ;; 8-Aug-95        Jack Repenning <jackr@sgi.com>
  89. ;;    Fix documentation of `ksh-align-to-keyword' to conform to the 23
  90. ;;    Feb default change.  Search for keywords obeying case, since the
  91. ;;    shell does.
  92. ;;
  93. ;; 23-Feb-1995        Gary Ellison    
  94. ;;    Merged Jonathan Stigelman <Stig@hackvan.com> into 2.5 souce.
  95. ;;
  96. ;; 23 Feb 1995          Jonathan Stigelman <Stig@hackvan.com>
  97. ;;    Reshuffled documentation to make the format more consistant with other
  98. ;;    elisp.  Added autoload and removed autoloading instructions from the
  99. ;;    ksh-mode docstring.  Changed default value for `ksh-align-to-keyword'
  100. ;;    to nil because it doesn't work properly.
  101. ;;
  102. ;; 2-Aug-1994        Gary Ellison    
  103. ;;    Last Modified: Mon Jun 13 16:52:55 1994 #29 (Gary Ellison)
  104. ;;    - Syntax table modifications to better support sexp navigation and
  105. ;;      parsing.
  106. ;;    - Fixed keyword regexps. Keywords were not being recoginized on the
  107. ;;      same line as " ' `.
  108. ;;
  109. ;; 13-Jun-1994        Gary Ellison    
  110. ;;    Last Modified: Wed Mar 30 14:12:26 1994 #28 (Gary Ellison)
  111. ;;    - Minor excursion problem fixed in ksh-indent-command.
  112. ;;
  113. ;; 30-Mar-1994        Gary Ellison    
  114. ;;    Last Modified: Fri Mar 25 15:42:29 1994 #25 (Gary Ellison)
  115. ;;    - Implement user customizable ksh-comment-regexp.
  116. ;;    - Make the keyword vs line indentation alignment customizable
  117. ;;      by calling ksh-align-to-keyword based on variable of same
  118. ;;      name. (If the code is obfuscated or convoluted I can attribute
  119. ;;      this to a severe head cold and not malice :)
  120. ;;
  121. ;; 25-Mar-1994        Gary Ellison    
  122. ;;    Last Modified: Fri Feb  4 13:06:30 1994 #23 (Gary Ellison)
  123. ;;    - Nest relative to the line indentation not the keywords
  124. ;;      column.
  125. ;;
  126. ;; 4-Feb-1994        Gary Ellison    
  127. ;;    Last Modified: Wed Nov 10 10:03:01 1993 #18 (Gary Ellison)
  128. ;;    - Add direct support for font-lock-mode. Thanks Espen Skoglund
  129. ;;      for the regular expressions.
  130. ;;
  131. ;; 10-Nov-1993        Gary Ellison    
  132. ;;    Last Modified: Tue Oct 12 15:23:06 1993 #17 (Gary Ellison)
  133. ;;    Fix message on ksh-match-and-tell to not get invalid format
  134. ;;    when a % appears in the string.
  135. ;;
  136. ;; 12-Oct-1993        Espen Skoglund <espensk@stud.cs.uit.no>.
  137. ;;    Last Modified: Tue Oct 12 15:03:01 1993 #16 (Gary Ellison)
  138. ;;    Apply Line continuation patch supplied by Espen Skoglund
  139. ;;
  140. ;; 1-Sep-1993        Gary Ellison    
  141. ;;    Last Modified: Tue Aug 17 17:18:18 1993 #14 (Gary Ellison)
  142. ;;    Get rid of this-line hack in ksh-get-nester-column.
  143. ;;
  144. ;; 17-Aug-1993        Gary Ellison    
  145. ;;    Last Modified: Mon Jun 21 14:00:43 1993 #13 (Gary Ellison)
  146. ;;    Code uses builtin current-indentation instead of lisp defun
  147. ;;    ksh-indentation-on-this-line (thanks to Tom Tromey).
  148. ;;    More and better doc strings.
  149. ;;
  150. ;; 5-Aug-1993        Tom Tromey <tromey@cns.caltech.edu>
  151. ;;    Last Modified: Thu Aug  5 11:09:12 1993 #12 (Tom Tromey)
  152. ;;    ksh-indent-region skips blank lines.  Uses let binding instead
  153. ;;    of setq.  No longer marks buffer modified if indentation
  154. ;;    doesn't change. 
  155. ;;
  156. ;; 21-Jun-1993        Gary Ellison    
  157. ;;    Last Modified: Mon Mar 29 15:05:34 1993 #11 (Gary Ellison)
  158. ;;    Use make-local-variables instead of make-variables-buffer-local
  159. ;;    ksh-indent now supports nil (keyword aligned) or number (offset)
  160. ;;    Support ksh-tab-always-indent feature
  161. ;;    Variables offsetting indentation renamed to better reflect their
  162. ;;    role.
  163. ;;    Integrate keyword completion feature supplied by
  164. ;;    Haavard Rue <hrue@imf.unit.no>.
  165. ;;
  166. ;; 29-Mar-1993        Gary Ellison    
  167. ;;    Last Modified: Tue Sep 29 16:14:02 1992 #10 (Gary Ellison)
  168. ;;    Integrate line continuation patch supplied by
  169. ;;    Haavard Rue <hrue@imf.unit.no>
  170. ;;    Name back to ksh-mode to avoid confusion with sh-mode
  171. ;;    by Thomas W. Strong, Jr. <strong+@cmu.edu>.
  172. ;;
  173. ;; 29-Sep-1992        Gary Ellison    
  174. ;;    Last Modified: Wed Sep  2 08:51:40 1992 #9 (Gary Ellison)
  175. ;;    Full support of ksh88 case items. 
  176. ;;    Align statements under "do" and "then" keywords one position 
  177. ;;    past the keyword.
  178. ;;
  179. ;; 2-Sep-1992        Gary Ellison    
  180. ;;    Last Modified: Tue Aug  4 14:34:35 1992 #8 (Gary Ellison)
  181. ;;    Use make-variable-buffer-local instead of make-local-variable
  182. ;;    Get rid of superflous ksh-default variables.
  183. ;;    Use end of word match \b for "then", "do", "else", "elif"
  184. ;;    Support process substitution lists and exclude ksh 88 case items
  185. ;;    Use default-tab-width for indentation defaults.
  186. ;;    Moved installation instructions to the mode level documentation 
  187. ;;    section.
  188. ;;    Fixed auto-mode-alist documentation.
  189. ;;
  190. ;; 24-Jul-1992        Gary Ellison    
  191. ;;    Last Modified: Fri Jul 24 09:45:11 1992 #7 (Gary Ellison)
  192. ;;    Modified ksh-indent-region to use marker versus fixed end point.
  193. ;;    comment-start-skip regexp no longer fooled by parameter substitution.
  194. ;;    Added constant ksh-mode-version.
  195. ;;
  196. ;; 21-Jul-1992        Gary Ellison    
  197. ;;    Last Modified: Tue Jul 21 15:53:57 1992 #6 (Gary Ellison)
  198. ;;    Indent with tabs instead of spaces.
  199. ;;    Can handle just about all styles.
  200. ;;    Anti-newline in REs.
  201. ;;    Word delim "\b" in REs
  202. ;;    More syntax entries.
  203. ;;    Variables with regexp suffix abbreviated to re
  204. ;;    Better } handling
  205. ;;    Implemented minimal indent-region-function
  206. ;;    Mode documentation corrected.
  207. ;;    Minor lisp source format changes.
  208. ;;    
  209. ;; 29-Jun-1992        Gary Ellison    
  210. ;;    Last Modified: Mon Jun 29 15:39:35 1992 #5 (Gary Ellison)
  211. ;;    Optimize line-to-string
  212. ;;    Implicit/Explicit functions aok
  213. ;;    More indentation variables
  214. ;;    Superfluous defun killed.
  215. ;;    renamed to sh-mode
  216. ;;    
  217. ;; 22-Jun-1992          Gary Ellison
  218. ;;    Last Modified: Mon Jun 22 15:01:14 1992 #4 (Gary Ellison)
  219. ;;    Cleanup pre att.emacs posting
  220. ;;
  221. ;; 19-Jun-1992          Gary Ellison
  222. ;;    Last Modified: Fri Jun 19 17:19:14 1992 #3 (Gary Ellison)
  223. ;;    Minimal case indent handling
  224. ;;
  225. ;; 19-Jun-1992          Gary Ellison
  226. ;;    Last Modified: Fri Jun 19 16:23:26 1992 #2 (Gary Ellison)
  227. ;;    Nesting handled except for case statement
  228. ;;
  229. ;; 19-Jun-1992          Gary Ellison
  230. ;;    Last Modified: Fri Jun 19 10:03:07 1992 #1 (Gary Ellison)
  231. ;;    Conception of this mode.
  232. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  233.  
  234. (defconst ksh-mode-version "$Revision: 2.9 $"
  235.   "*Version numbers of this version of ksh-mode")
  236.  
  237. ;;
  238. ;; Variables controlling indentation style
  239. ;;
  240.  
  241. (defvar ksh-indent 2 
  242.   ;; perhaps c-basic-offset would be okay to use as a default, but using
  243.   ;; default-tab-width as the default is ridiculous --Stig
  244.   "*Indentation of ksh statements with respect to containing block. A value
  245. of nil indicates compound list keyword \(\"do\" and \"then\"\) alignment.")
  246. (defvar ksh-case-item-offset ksh-indent
  247.   "*Additional indentation for case items within a case statement.")
  248. (defvar ksh-case-indent nil
  249.   "*Additional indentation for statements under case items.")
  250. (defvar ksh-group-offset (- ksh-indent)
  251.   "*Additional indentation for keywords \"do\" and \"then\".")
  252. (defvar ksh-brace-offset 0
  253.   "*Additional indentation of \"{\" under functions or brace groupings.")
  254. (defvar ksh-multiline-offset 1
  255.   "*Additional indentation of line that is preceded of a line ending with a
  256. \\ to make it continue on next line.")
  257. (defvar ksh-match-and-tell t
  258.   "*If non-nil echo in the minibuffer the matching compound command
  259. for the \"done\", \"}\", \"fi\", or \"esac\". ")
  260. (defvar ksh-tab-always-indent t
  261.   "*Controls the operation of the TAB key. If t (the default), always
  262. reindent the current line.  If nil, indent the current line only if
  263. point is at the left margin or in the line's indentation; otherwise
  264. insert a tab.")
  265.  
  266. (defvar ksh-align-to-keyword nil
  267.   ;; #### - this is broken, so it should be disabled by default --Stig
  268.   "*Controls whether nested constructs align from the keyword or
  269. the current indentation. If non-nil, indentation will be relative to
  270. the column the keyword starts. If nil, indentation will be relative to
  271. the current indentation of the line the keyword is on.
  272. The default value is non-nil.  The non-nil case doesn't work very well.")
  273.  
  274. (defvar ksh-comment-regexp "^\\s *#"
  275.   "*Regular expression used to recognize comments. Customize to support
  276. ksh-like languages.")
  277.  
  278. (defun ksh-current-indentation ()
  279.   nil
  280.   )
  281. ;;
  282. (fset 'ksh-current-indentation 'current-column)
  283. ;;
  284. ;; Variables controlling completion
  285. (defvar ksh-completion-list '())
  286. (make-variable-buffer-local 'ksh-completion-list)
  287. (set-default 'ksh-completion-list  '())
  288.  
  289. ;;
  290. ;; -type-  : type number, 0:misc, 1:variable, 2:function
  291. ;; -regexp-: regexp used to parse the script
  292. ;; -match- : used by match-beginning/end to pickup target
  293. ;;
  294. (defvar ksh-completion-type-misc 0)
  295. (defvar ksh-completion-regexp-var "\\([A-Za-z_0-9]+\\)=")
  296. (defvar ksh-completion-type-var 1)
  297. (defvar ksh-completion-match-var 1) 
  298. (defvar ksh-completion-regexp-var2 "\\$\\({\\|{#\\)?\\([A-Za-z_0-9]+\\)[#%:}]?")
  299. (defvar ksh-completion-match-var2 2)
  300. (defvar ksh-completion-regexp-function
  301.   "\\(function\\)?[ \t]*\\([A-Za-z_0-9]+\\)[ \t]*([ \t]*)")
  302. (defvar ksh-completion-type-function 2)
  303. (defvar ksh-completion-match-function 2)
  304.  
  305. ;;
  306. ;; Variable controlling fontification
  307. ;;
  308. (defvar ksh-keywords '("for" "in" "do" "done" "select" "case" "esac" "if"
  309. "then" "elif" "else" "fi" "while" "until" "function" "time"
  310. "alias" "bg" "break" "continue" "cd" "exit" "echo" "fc" "fg" "getopts" "jobs"
  311. "kill" "let" "newgrp" "print" "pwd" "read" "readonly" "return" "set" "shift"
  312. "test" "times" "trap" "typeset" "ulimit" "umask" "unalias" "unset" "wait" "whence"))
  313.  
  314. ;;       '("\\<function[ \t]+\\([^(; \t]+\\)" 1 font-lock-function-name-face)
  315. (defconst ksh-font-lock-keywords
  316.       (list
  317.        ;; Fontify [[ ]] expressions
  318.        '("\\(\\[.*\\]\\)"  1 font-lock-string-face t)
  319.        ;; Fontify keywords
  320.        (cons (concat
  321.           "\\(\\<"
  322.           (mapconcat 'identity ksh-keywords "\\>\\|\\<")
  323.           "\\>\\)")
  324.          1)
  325.        ;; Fontify function names
  326.        '("\\<function[ \t]+\\([^(; \t]+\\)" 1 font-lock-function-name-face)
  327.        '("\\(^[ \t]*[A-Za-z_][A-Za-z_0-9]*[ \t]*()\\)" 1 font-lock-function-name-face)
  328.        ))
  329.  
  330. ;; XEmacs addition
  331. (put 'ksh-mode    'font-lock-keywords 'ksh-font-lock-keywords)
  332.  
  333. ;;
  334. ;; Context/indentation regular expressions
  335. ;; 
  336. ;; indenting expressions
  337. ;;
  338. ;(defconst ksh-then-do-re     "^[^#\n]*\\s\"*\\b\\(then\\|do\\)\\b"
  339. (defconst ksh-then-do-re     "\\s *\\b\\(then\\|do\\)\\b"
  340.   "*Regexp used to locate grouping keywords: \"then\" and \"do\"" )
  341.  
  342. (defconst ksh-do-re          "\\s *\\bdo\\(\\b\\|$\\)"
  343.   "*Regexp used to match keyword: do")
  344.  
  345. (defconst ksh-then-re        "\\s *\\bthen\\(\\b\\|$\\)"
  346.   "*Regexp used to match keyword: then")
  347.  
  348. ;;
  349. ;; Structure starting/indenting keywords
  350. ;;
  351. (defconst ksh-else-re           "\\s *\\belse\\(\\b\\|$\\)"
  352.   "*Regexp used to match keyword: else")
  353.  
  354. (defconst ksh-elif-re           "\\s *\\belif\\(\\b\\|$\\)"
  355.   "*Regexp used to match keyword: elif")
  356.  
  357. (defconst ksh-brace-re           "\\S>*{[ \t\n]"
  358.   "*Regexp used to match syntactic entity: { ")
  359.  
  360. (defconst ksh-case-item-end-re           "\\S>*;;[ \t\n]"
  361.   "*Regexp used to match case item end syntactic entity: ;;")
  362.  
  363. (defconst ksh-keywords-re
  364.   "\\s *\\b\\(else\\|if\\|elif\\|case\\|while\\|for\\|until\\|select\\)\\b"
  365.   "*Regexp used to detect compound command keywords: if, else, elif case, 
  366. while, for, until, and select")
  367.  
  368.  
  369. (defconst ksh-if-re         "\\s *\\b\\(if\\)\\b"
  370.   "*Regexp used to match keyword: if")
  371.  
  372. (defconst ksh-iteration-keywords-re 
  373.   "\\s *\\b\\(while\\|for\\|until\\|select\\)\\b"
  374.   "*Match one of the keywords: while, until, for, select")
  375.  
  376. (defconst ksh-case-re           "\\s *\\bcase\\b"
  377.   "*Regexp used to match keyword: case")
  378.  
  379. (defconst ksh-explicit-func-re
  380.   "^\\s *\\(function\\s [a-zA-z_][a-zA-Z0-1_]*\\)\\b"
  381.   "*Match an explicit function definition: function name")
  382.  
  383. (defconst ksh-implicit-func-re
  384.   "^\\s *\\([a-zA-z_][a-zA-Z0-1_]*\\)\\s *()\\s *"
  385.   "*Match an implicit function definition: name ()")
  386.  
  387. (defconst ksh-func-brace-re "^\\s *\\(.*{\\)[ \t\n]+"
  388.   "*Match a implicit function definition brace: name { ")
  389.  
  390. ;;
  391. ;; indenting 
  392. (defconst ksh-case-item-re           "^[^#\n]*\\s\"*\\()\\)"
  393.   "*Regexp used to match case-items including ksh88")
  394.  
  395. (defconst ksh-paren-re           "^[^#\n]*\\s\"*)[ \t\n]+"
  396.   "*Regexp used to match compound list & case items")
  397.  
  398. ;;
  399. ;; structure ending keyword regular expressions
  400. (defconst ksh-fi-re            "\\s *\\bfi\\b"
  401.   "*Regexp used to match keyword: fi")
  402.  
  403. (defconst ksh-esac-re          "\\s *\\besac\\b"
  404.   "*Regexp used to match keyword: esac")
  405.  
  406. (defconst ksh-done-re          "\\s *\\bdone\\b"
  407.   "*Regexp used to match keyword: done")
  408.  
  409. (defconst ksh-brace-end-re  "\\s *}\\s *"
  410.   "*Regexp used to match function brace-groups")
  411.  
  412. (defconst ksh-multiline-re "^.*\\\\$"
  413.   "*Regexp used to match a line with a statement using more lines.")
  414.  
  415. ;;
  416. ;;
  417. ;; Create mode specific tables
  418. (defvar ksh-mode-syntax-table nil
  419.   "Syntax table used while in ksh mode.")
  420. (if ksh-mode-syntax-table
  421.     ()
  422.   (setq ksh-mode-syntax-table (make-syntax-table (standard-syntax-table)))
  423.   (modify-syntax-entry ?\( "." ksh-mode-syntax-table)
  424.   (modify-syntax-entry ?\) "." ksh-mode-syntax-table)
  425.   (modify-syntax-entry ?{ "." ksh-mode-syntax-table)
  426.   (modify-syntax-entry ?} "." ksh-mode-syntax-table)
  427.   (modify-syntax-entry ?\[ "(]" ksh-mode-syntax-table)
  428.   (modify-syntax-entry ?\] ")[" ksh-mode-syntax-table)
  429.   (modify-syntax-entry ?\' "\"" ksh-mode-syntax-table)
  430.   (modify-syntax-entry ?` "\"" ksh-mode-syntax-table)
  431.   (modify-syntax-entry ?\n ">" ksh-mode-syntax-table)
  432.   (modify-syntax-entry ?\f ">" ksh-mode-syntax-table)
  433.   (modify-syntax-entry ?# "<" ksh-mode-syntax-table)
  434.   (modify-syntax-entry ?_ "_" ksh-mode-syntax-table)
  435.   (modify-syntax-entry ?< "." ksh-mode-syntax-table)
  436.   (modify-syntax-entry ?> "." ksh-mode-syntax-table)
  437.   (modify-syntax-entry ?& "." ksh-mode-syntax-table)
  438.   (modify-syntax-entry ?| "." ksh-mode-syntax-table)
  439.   (modify-syntax-entry ?$ "\\" ksh-mode-syntax-table)
  440.   (modify-syntax-entry ?% "." ksh-mode-syntax-table)
  441.   (modify-syntax-entry ?= "." ksh-mode-syntax-table)
  442.   (modify-syntax-entry ?/ "." ksh-mode-syntax-table)
  443.   (modify-syntax-entry ?+ "." ksh-mode-syntax-table)
  444.   (modify-syntax-entry ?* "." ksh-mode-syntax-table)
  445.   (modify-syntax-entry ?- "." ksh-mode-syntax-table)
  446.   (modify-syntax-entry ?\; "." ksh-mode-syntax-table)
  447.   (modify-syntax-entry ?: "." ksh-mode-syntax-table)
  448.   )
  449.  
  450. (defvar ksh-mode-abbrev-table nil
  451.   "Abbrev table used while in ksh mode.")
  452. (define-abbrev-table 'ksh-mode-abbrev-table ())
  453.  
  454. (defvar ksh-mode-map nil 
  455.   "Keymap used in ksh mode")
  456.  
  457. (if ksh-mode-map
  458.     ()
  459.   (setq ksh-mode-map (make-sparse-keymap))
  460.   (define-key ksh-mode-map "\t"    'ksh-indent-command)
  461. ;;  (define-key ksh-mode-map "\t"    'ksh-indent-line)
  462.   (define-key ksh-mode-map "\C-j"    'reindent-then-newline-and-indent)
  463.   (define-key ksh-mode-map "\e\t"    'ksh-complete-symbol)
  464.   (define-key ksh-mode-map "\C-c\t"    'ksh-completion-init-and-pickup)
  465.   )
  466.  
  467.  
  468. ;;;###autoload
  469. (defun ksh-mode ()
  470.   "ksh-mode $Revision: 2.9 $ - Major mode for editing (Bourne, Korn or Bourne again)
  471. shell scripts.
  472. Special key bindings and commands:
  473. \\{ksh-mode-map}
  474. Variables controlling indentation style:
  475. ksh-indent
  476.     Indentation of ksh statements with respect to containing block.
  477.     Default value is 2.
  478. ksh-case-indent
  479.     Additional indentation for statements under case items.
  480.     Default value is nil which will align the statements one position 
  481.     past the \")\" of the pattern.
  482. ksh-case-item-offset
  483.     Additional indentation for case items within a case statement.
  484.     Default value is 2.
  485. ksh-group-offset
  486.     Additional indentation for keywords \"do\" and \"then\".
  487.     Default value is -2.
  488. ksh-brace-offset
  489.     Additional indentation of \"{\" under functions or brace groupings.
  490.     Default value is 0.
  491. ksh-multiline-offset
  492.    Additional indentation of line that is preceded of a line ending with a
  493.    \\ to make it continue on next line.
  494. ksh-tab-always-indent
  495.     Controls the operation of the TAB key. If t (the default), always
  496.     reindent the current line.  If nil, indent the current line only if
  497.     point is at the left margin or in the line's indentation; otherwise
  498.     insert a tab.
  499. ksh-match-and-tell
  500.     If non-nil echo in the minibuffer the matching compound command
  501.     for the \"done\", \"}\", \"fi\", or \"esac\". Default value is t.
  502.  
  503. ksh-align-to-keyword
  504.     Controls whether nested constructs align from the keyword or
  505.     the current indentation. If non-nil, indentation will be relative to
  506.     the column the keyword starts. If nil, indentation will be relative to
  507.     the current indentation of the line the keyword is on.
  508.     The default value is non-nil.
  509.  
  510. ksh-comment-regexp
  511.   Regular expression used to recognize comments. Customize to support
  512.   ksh-like languages. Default value is \"\^\\\\s *#\".
  513.  
  514. Style Guide.
  515.  By setting
  516.     (setq ksh-indent default-tab-width)
  517.     (setq ksh-group-offset 0)
  518.  
  519.     The following style is obtained:
  520.  
  521.     if [ -z $foo ]
  522.         then
  523.             bar    # <-- ksh-group-offset is additive to ksh-indent
  524.             foo
  525.     fi
  526.  
  527.  By setting
  528.     (setq ksh-indent default-tab-width)
  529.     (setq ksh-group-offset (- 0 ksh-indent))
  530.  
  531.     The following style is obtained:
  532.  
  533.     if [ -z $foo ]
  534.     then
  535.         bar
  536.         foo
  537.     fi
  538.  
  539.  By setting
  540.     (setq ksh-case-item-offset 1)
  541.     (setq ksh-case-indent nil)
  542.  
  543.     The following style is obtained:
  544.  
  545.     case x in *
  546.      foo) bar           # <-- ksh-case-item-offset
  547.           baz;;         # <-- ksh-case-indent aligns with \")\"
  548.      foobar) foo
  549.              bar;;
  550.     esac
  551.  
  552.  By setting
  553.     (setq ksh-case-item-offset 1)
  554.     (setq ksh-case-indent 6)
  555.  
  556.     The following style is obtained:
  557.  
  558.     case x in *
  559.      foo) bar           # <-- ksh-case-item-offset
  560.            baz;;        # <-- ksh-case-indent
  561.      foobar) foo
  562.            bar;;
  563.     esac
  564.     
  565.  
  566. Installation:
  567.  
  568.  (setq ksh-mode-hook
  569.       (function (lambda ()
  570.          (font-lock-mode 1)             ;; font-lock the buffer
  571.          (setq ksh-indent 8)
  572.      (setq ksh-group-offset -8)
  573.      (setq ksh-brace-offset -8)   
  574.          (setq ksh-tab-always-indent t)
  575.          (setq ksh-match-and-tell t)
  576.          (setq ksh-align-to-keyword t)    ;; Turn on keyword alignment
  577.      )))"
  578.   ;;
  579.   ;; and away we go
  580.   (interactive)
  581.   (kill-all-local-variables)
  582.   (use-local-map ksh-mode-map)
  583.   (setq major-mode 'ksh-mode)
  584.   (setq mode-name "Ksh")
  585.   (setq local-abbrev-table ksh-mode-abbrev-table)
  586.   (set-syntax-table ksh-mode-syntax-table)
  587.   (make-local-variable 'indent-line-function)
  588.   (setq indent-line-function 'ksh-indent-line)
  589.   (make-local-variable 'indent-region-function)
  590.   (setq indent-region-function 'ksh-indent-region)
  591.   (make-local-variable 'comment-start)
  592.   (setq comment-start "# ")
  593.   (make-local-variable 'comment-end)
  594.   (setq comment-end "")
  595.   (make-local-variable 'comment-column)
  596.   (setq comment-column 32)
  597.   (make-local-variable 'comment-start-skip)
  598.   (setq comment-start-skip "#+ *")
  599.   ;;
  600.   ;; config font-lock mode
  601.   (make-local-variable 'font-lock-keywords) 
  602.   (setq font-lock-keywords ksh-font-lock-keywords)
  603.   ;;
  604.   ;; Let the user customize
  605.   (run-hooks 'ksh-mode-hook)
  606.   (if (not ksh-align-to-keyword)
  607.       (ksh-align-to-keyword -1)
  608.     )
  609.   ) ;; defun
  610.  
  611. ;;
  612. ;; Support functions
  613.  
  614. (defun ksh-align-to-keyword (&optional arg)
  615.   "Toggle value of ksh-align-to-keyword and rebind the ksh-current-indentation
  616. function. With arg, force alignment to keyword if and only if arg is positive."
  617.   (interactive)
  618.   (if (null arg)            ;just toggle
  619.       (cond ((not ksh-align-to-keyword)
  620.          (setq ksh-align-to-keyword t)
  621.          (fset 'ksh-current-indentation 'current-column))
  622.         (t
  623.          (setq ksh-align-to-keyword nil)
  624.          (fset 'ksh-current-indentation 'current-indentation))
  625.         )
  626.     (cond ((natnump arg)
  627.        (setq ksh-align-to-keyword t)
  628.        (fset 'ksh-current-indentation 'current-column))
  629.       (t
  630.        (setq ksh-align-to-keyword nil)
  631.        (fset 'ksh-current-indentation 'current-indentation))
  632.       ))
  633.   )
  634.  
  635. (defun ksh-current-line ()
  636.   "Return the vertical position of point in the buffer.
  637. Top line is 1."
  638.   (+ (count-lines (point-min) (point))
  639.      (if (= (current-column) 0) 1 0))
  640.   )
  641.  
  642.  
  643. (defun ksh-line-to-string ()
  644.   "From point, construct a string from all characters on
  645. current line"
  646.   (skip-chars-forward " \t") ;; skip tabs as well as spaces
  647.   (buffer-substring (point)
  648.                     (progn
  649.                       (end-of-line 1)
  650.                       (point))))
  651.  
  652. (defun ksh-get-nest-level ()
  653.   "Return a 2 element list (nest-level nest-line) describing where the
  654. current line should nest."
  655.   (let ((case-fold-search)
  656.     (level))
  657.     (save-excursion
  658.       (forward-line -1)
  659.       (while (and (not (bobp))
  660.           (null level))
  661.     (if (and (not (looking-at "^\\s *$"))
  662.           (not (save-excursion
  663.              (forward-line -1)
  664.              (beginning-of-line)
  665.             (looking-at ksh-multiline-re)))
  666.          (not (looking-at ksh-comment-regexp)))
  667.         (setq level (cons (current-indentation)
  668.                   (ksh-current-line)))
  669.       (forward-line -1)
  670.       );; if
  671.     );; while
  672.       (if (null level)
  673.       (cons (current-indentation) (ksh-current-line))
  674.     level)
  675.       )
  676.     )
  677.   )
  678.  
  679. (defun ksh-looking-at-compound-list ()
  680.   "Return true if current line contains compound list initiating keyword"
  681.   (or 
  682.    (looking-at ksh-do-re)
  683.    (looking-at ksh-then-re)
  684.    ) ;; or
  685.   ) ;; defun
  686.  
  687. (defun ksh-looking-at-case-item ()
  688.   "Return true if current line is a case-item .vs. paren compound list"
  689.   (save-excursion
  690.     (beginning-of-line)
  691.     ;;
  692.     ;; Handle paren indentation constructs for this line
  693.     (cond ((looking-at ksh-paren-re)
  694.        (goto-line (cdr (ksh-get-nest-level)))
  695.        ;;
  696.        ;; The question is whether this is really a case item or just
  697.        ;; parenthesized compound list.
  698.        (cond ((or (looking-at ksh-case-re)
  699.               (looking-at ksh-case-item-end-re)))
  700.          ;;
  701.          ;; turns out to be a parenthesized compound list
  702.          ;; so propigate the nil for cond
  703.          )
  704.        ))
  705.     )
  706.   ) ;; defun
  707.  
  708.  
  709. (defun ksh-get-case-indent ()
  710.   "Return the column of the closest open case statement"
  711.   (save-excursion
  712.     (let (
  713.       (nest-list (ksh-get-compound-level ksh-case-re ksh-esac-re (point)))
  714.       )
  715.       (if (null nest-list)
  716.       (progn 
  717.         (if ksh-match-and-tell
  718.         (message "No matching case for ;;"))
  719.         0)
  720.     (car nest-list)))
  721.     )
  722.   )
  723.  
  724.  
  725. ;;
  726. ;; Functions which make this mode what it is
  727. ;;
  728.  
  729. (defun ksh-get-nester-column (nest-line)
  730.   "Return the column to indent to with respect to nest-line taking 
  731. into consideration keywords and other nesting constructs."
  732.   (save-excursion 
  733.     (let ((fence-post)
  734. ;      (start-post)
  735.       (nester-column)
  736.        (case-fold-search)
  737.       (start-line (ksh-current-line)))
  738.       (cond
  739.        ;;
  740.        ;; Handle case item indentation constructs for this line
  741.        ((ksh-looking-at-case-item)
  742.     (save-excursion
  743.       (goto-line nest-line)
  744.       (back-to-indentation)
  745.       (let ((fence-post (ksh-eol-point)))
  746.         ;;
  747.         ;; Now know there is a case-item so detect whether
  748.         ;; it is first under case, just another case-item, or
  749.         ;; a case-item and case-item-end all rolled together.
  750.         ;;
  751.         (cond ((ksh-search-forward-sexp ksh-case-re fence-post)
  752.            (+ (ksh-current-indentation) ksh-case-item-offset))
  753.           
  754.           ((ksh-looking-at-case-item)
  755.            (current-indentation))
  756.           
  757.           ((looking-at ksh-case-item-end-re)
  758.            (end-of-line)
  759.            (+ (ksh-get-case-indent) ksh-case-item-offset))
  760.           )
  761.         )))
  762.        (t;; Not a case-item.  What to do relative to the nest-line?
  763.     (save-excursion
  764.       (goto-line nest-line)
  765. ;      (setq start-post (point))
  766.       (setq fence-post (ksh-eol-point))
  767.       (setq nester-column
  768.         (save-excursion
  769.           (cond
  770.            ;;
  771.            ;; Check if we are in a continued statement
  772.            ((and (looking-at ksh-multiline-re)
  773.              (save-excursion
  774.                (goto-line (1- start-line))
  775.                (looking-at ksh-multiline-re)))
  776.             (+ (current-indentation) ksh-multiline-offset))
  777.            ;; In order to locate the column of the keyword,
  778.            ;; which might be embedded within a case-item,
  779.            ;; it is necessary to iterate over sexp.
  780.            ((progn
  781.               (save-excursion
  782.             (back-to-indentation)
  783.             (if (ksh-search-forward-sexp ksh-keywords-re fence-post)
  784.                 (progn
  785.                   ;;
  786.                   ;; special pun intended 'case'
  787.                   (if (looking-at ksh-case-re)
  788.                   (+ (ksh-current-indentation)
  789.                      ksh-case-item-offset)
  790.                 (+ (ksh-current-indentation)
  791.                    (if (null ksh-indent)
  792.                        2 ksh-indent))))    
  793.               nil))
  794.               ))
  795.            ;;
  796.            ;;  handle then or do
  797.            ((progn
  798.               (save-excursion
  799.             (back-to-indentation)
  800.             (if (ksh-search-forward-sexp ksh-then-do-re fence-post)
  801.                 (progn
  802.                   (if (null ksh-indent)
  803.                   (+ (ksh-current-indentation) 1)
  804.                 (+ (ksh-current-indentation) ksh-indent)))
  805.               nil))))
  806.  
  807.            ((looking-at ksh-brace-re)
  808.             (+ (current-indentation)
  809.                (if (null ksh-indent)
  810.                2 ksh-indent)
  811.                ))
  812.            ;;
  813.            ;; Forces functions to first column
  814.            ((or (looking-at ksh-implicit-func-re)
  815.             (looking-at ksh-explicit-func-re))
  816.             (if (looking-at ksh-func-brace-re)
  817.             (if (null ksh-indent)
  818.                 2 ksh-indent)
  819.               ksh-brace-offset))
  820.  
  821.            ;;
  822.            ;; Need to first detect the end of a case-item
  823.            ((looking-at ksh-case-item-end-re)
  824.             (end-of-line)
  825.             (+ (ksh-get-case-indent) ksh-case-item-offset))
  826.            ;;
  827.            ;; Now detect first statement under a case item
  828.            ((ksh-looking-at-case-item)
  829.             (if (null ksh-case-indent)
  830.             (progn
  831.               (re-search-forward ksh-case-item-re fence-post t)
  832.               (goto-char (match-end 1))
  833.               (+ (current-column) 1))
  834.               (+ (current-indentation) ksh-case-indent)))
  835.            
  836.            ;; This is hosed when using current-column
  837.            ;; and there is a multi-command expression as the
  838.            ;; nester.
  839.            (t (current-indentation)))
  840.           )
  841.         ));; excursion over
  842.     ;;
  843.     ;; Handle additional indentation constructs for this line
  844.     (cond ((ksh-looking-at-compound-list)
  845.            (+ nester-column ksh-group-offset))
  846.           ((looking-at ksh-brace-re)
  847.            (+ nester-column ksh-brace-offset))
  848.           (t nester-column))
  849.     );; Not a case-item
  850.        )
  851.       );;let
  852.     );; excursion
  853.   ) ;; defun
  854.  
  855. (defun ksh-indent-command ()
  856.   "Indent current line relative to containing block and allow for
  857. ksh-tab-always-indent customization"
  858.   (interactive)
  859.   (let (case-fold-search)
  860.     (cond ((save-excursion
  861.          (skip-chars-backward " \t")
  862.          (bolp))
  863.        (ksh-indent-line))
  864.       (ksh-tab-always-indent
  865.        (save-excursion
  866.          (ksh-indent-line)))
  867.       (t (insert-tab))
  868.       ))
  869.   )
  870.  
  871.  
  872. (defun ksh-indent-line ()
  873.   "Indent current line as far as it should go according
  874. to the syntax/context"
  875.   (interactive)
  876.   (let (case-fold-search)
  877.     (save-excursion
  878.       (beginning-of-line)
  879.       (if (bobp)
  880.       nil
  881.     ;;
  882.     ;; Align this line to current nesting level
  883.     (let*
  884.         (
  885.          (level-list (ksh-get-nest-level)) ; Where to nest against
  886.          ;;           (last-line-level (car level-list))
  887.          (this-line-level (current-indentation))
  888.          (nester-column (ksh-get-nester-column (cdr level-list)))
  889.          (struct-match (ksh-match-structure-and-reindent))
  890.          )
  891.       (if struct-match
  892.           (setq nester-column struct-match))
  893.       (if (eq nester-column this-line-level)
  894.           nil
  895.         (beginning-of-line)
  896.         (let ((beg (point)))
  897.           (back-to-indentation)
  898.           (delete-region beg (point)))
  899.         (indent-to nester-column))
  900.       );; let*
  901.     );; if
  902.       );; excursion
  903.     ;;
  904.     ;; Position point on this line
  905.     (let*
  906.     (
  907.      (this-line-level (current-indentation))
  908.      (this-bol (ksh-bol-point))
  909.      (this-point (- (point) this-bol))
  910.      )
  911.       (cond ((> this-line-level this-point);; point in initial white space
  912.          (back-to-indentation))
  913.         (t nil)
  914.         );; cond
  915.       );; let*
  916.     );; let
  917.   );; defun
  918.  
  919.  
  920. (defun ksh-match-indent-level (begin-re end-re)
  921.   "Match the compound command and indent. Return nil on no match,
  922. indentation to use for this line otherwise."
  923.   (interactive)
  924.   (let* ((case-fold-search)
  925.      (nest-list 
  926.       (save-excursion
  927.         (ksh-get-compound-level begin-re end-re (point))
  928.         ))
  929.      ) ;; bindings
  930.     (if (null nest-list)
  931.     (progn
  932.       (if ksh-match-and-tell
  933.           (message "No matching compound command"))
  934.       nil) ;; Propagate a miss.
  935.       (let* (
  936.          (nest-level (car nest-list))
  937.          (match-line (cdr nest-list))
  938.          ) ;; bindings
  939.     (if ksh-match-and-tell
  940.         (save-excursion
  941.           (goto-line match-line)
  942.           (message "Matched ... %s" (ksh-line-to-string))
  943.           ) ;; excursion
  944.       ) ;; if ksh-match-and-tell
  945.     nest-level ;;Propagate a hit.
  946.     ) ;; let*
  947.       ) ;; if
  948.     ) ;; let*
  949.   ) ;; defun ksh-match-indent-level
  950.  
  951. (defun ksh-match-structure-and-reindent ()
  952.   "If the current line matches one of the indenting keywords
  953. or one of the control structure ending keywords then reindent. Also
  954. if ksh-match-and-tell is non-nil the matching structure will echo in
  955. the minibuffer"
  956.   (interactive)
  957.   (let (case-fold-search)
  958.     (save-excursion
  959.       (beginning-of-line)
  960.       (back-to-indentation)
  961.       (cond ((looking-at ksh-else-re)
  962.          (ksh-match-indent-level ksh-if-re ksh-fi-re))
  963.         ((looking-at ksh-elif-re)
  964.          (ksh-match-indent-level ksh-if-re ksh-fi-re))
  965.         ((looking-at ksh-fi-re)
  966.          (ksh-match-indent-level ksh-if-re ksh-fi-re))
  967.         ((looking-at ksh-done-re)
  968.          (ksh-match-indent-level ksh-iteration-keywords-re ksh-done-re))
  969.         ((looking-at ksh-esac-re)
  970.          (ksh-match-indent-level ksh-case-re ksh-esac-re))
  971.         ;;
  972.         ((looking-at ksh-brace-end-re)
  973.          (cond
  974.           ((ksh-match-indent-level ksh-implicit-func-re ksh-brace-end-re))
  975.           ((ksh-match-indent-level ksh-explicit-func-re ksh-brace-end-re))
  976.           ((ksh-match-indent-level ksh-func-brace-re ksh-brace-end-re))
  977.           (t nil)))
  978.         (t nil)
  979.         );; cond
  980.       )
  981.     ))
  982.  
  983.  
  984. (defun ksh-search-backward-sexp (sexp-re fence-post)
  985.   (let
  986.       ((old-pnt (point))
  987.        (sentinal nil)
  988.        )
  989.     (while
  990.     (progn
  991.       (if (not sentinal)
  992.           (backward-sexp 1))
  993.       (and (> (point) fence-post)
  994.            (not sentinal))
  995.       )
  996.       (if (looking-at sexp-re)
  997.       (save-excursion            ;avoid comment foolage
  998.         (let ((key-fence (point)))
  999.           (beginning-of-line)
  1000.           (back-to-indentation)
  1001.           (while (and (ksh-search-forward-sexp sexp-re key-fence)
  1002.               (< (point) key-fence)))
  1003.           
  1004.           (if (= key-fence (point))
  1005.           (setq sentinal t))
  1006.           ))
  1007.     ))
  1008.       
  1009.       (if (< (point) fence-post)
  1010.       (progn (goto-char old-pnt)
  1011.          nil)
  1012.     t)
  1013.       ))
  1014.  
  1015. (defun ksh-forward-sexp ()
  1016.   "Special incantation to march over syntax expressions and
  1017. avoid all sorts of nonsense"
  1018.   (if (char-equal ?< (char-syntax (char-after (point))))
  1019.       (end-of-line)
  1020.     (if (char-equal ?. (char-syntax (char-after (point))))
  1021.     (forward-char)
  1022.       (forward-sexp 1))
  1023.     )
  1024.   (if (eolp)
  1025.       (forward-line))
  1026.   (skip-chars-forward ") \t")        ;damn case
  1027.   )
  1028.  
  1029. (defun ksh-search-forward-sexp (sexp-re fence-post)
  1030.   "Search for an sexp. Return t on success with point at the
  1031. beginning of the sexp. Return nil on failure and restoring point
  1032. to it's original position"
  1033.   (let
  1034.       ((old-pnt (point))
  1035.        )
  1036.     (while (and (< (point) fence-post)
  1037.         (not (looking-at sexp-re)))
  1038.       (ksh-forward-sexp))
  1039.     
  1040.     (if (> (point) fence-post)
  1041.     (progn (goto-char old-pnt)
  1042.            nil)
  1043.       t)
  1044.     ))
  1045.  
  1046.  
  1047. (defun ksh-get-compound-level
  1048.   (begin-re end-re anchor-point &optional balance-list)
  1049.   "Determine how much to indent this structure. Return a list (level line) 
  1050. of the matching compound command or nil if no match found."
  1051.   (let* 
  1052.       (;; Locate the next compound begin keyword bounded by point-min
  1053.        (match-point
  1054.     (if (and (ksh-search-backward-sexp begin-re (point-min))
  1055.          (>= (point) (point-min))
  1056.          )
  1057.         (point)
  1058.       0))
  1059.  
  1060.        (nest-column (if (zerop match-point)
  1061.             1 
  1062.               (progn
  1063.             (goto-char match-point)
  1064.             (ksh-current-indentation))))
  1065.        (nest-list (cons 0 0))    ;; sentinel cons since cdr is >= 1
  1066.        )
  1067.     (if (zerop match-point)
  1068.     nil ;; graceful exit from recursion
  1069.       (progn
  1070.     (if (nlistp balance-list)
  1071.         (setq balance-list (list)))
  1072.     ;; Now search forward from matching start keyword for end keyword
  1073.     ;; which will locate interceding compound commands
  1074.     (while (and (consp nest-list) (zerop (cdr nest-list))
  1075.             (ksh-search-forward-sexp end-re anchor-point)
  1076.             (> anchor-point (point))
  1077.             )
  1078.       (if (not (memq (point) balance-list))
  1079.           (progn
  1080.         (setq balance-list (cons (point) balance-list))
  1081.         (goto-char match-point)  ;; beginning of compound cmd
  1082.         (setq nest-list
  1083.               (ksh-get-compound-level begin-re end-re
  1084.                           anchor-point balance-list))
  1085.         )
  1086.         (ksh-forward-sexp)
  1087.         ))
  1088.  
  1089.     (cond ((consp nest-list)
  1090.            (if (zerop (cdr nest-list))
  1091.          (progn
  1092.            (goto-char match-point)
  1093.            (cons nest-column (ksh-current-line)))
  1094.          nest-list))
  1095.           (t nil)
  1096.           )
  1097.     )
  1098.       )
  1099.     )
  1100.   )
  1101.  
  1102.  
  1103. (defun ksh-indent-region (start end)
  1104.   "From start to end, indent each line."
  1105.   ;; The algorithm is just moving through the region line by line with
  1106.   ;; the match noise turned off.  Only modifies nonempty lines.
  1107.   (save-excursion
  1108.     (let (ksh-match-and-tell
  1109.       (endmark (copy-marker end)))
  1110.       
  1111.       (goto-char start)
  1112.       (beginning-of-line)
  1113.       (setq start (point))
  1114.       (while (> (marker-position endmark) start)
  1115.     (if (not (and (bolp) (eolp)))
  1116.         (ksh-indent-line))
  1117.     (forward-line 1)
  1118.     (setq start (point)))
  1119.  
  1120.       (set-marker endmark nil)
  1121.       )
  1122.     )
  1123.   )
  1124.  
  1125. ;;
  1126. ;; Completion code supplied by Haavard Rue <hrue@imf.unit.no>.
  1127. ;;
  1128. ;;
  1129. ;; add a completion with a given type to the list
  1130. ;;
  1131. (defun ksh-addto-alist (completion type)
  1132.   (setq ksh-completion-list
  1133.     (append ksh-completion-list
  1134.         (list (cons completion type)))))
  1135. ;;
  1136. ;; init the list and pickup all 
  1137. ;;
  1138. (defun ksh-completion-init-and-pickup ()
  1139.   (interactive)
  1140.   (let (case-fold-search)
  1141.     (ksh-completion-list-init)
  1142.     (ksh-pickup-all)))
  1143.  
  1144. ;;
  1145. ;; init the list
  1146. ;;
  1147. (defun ksh-completion-list-init ()
  1148.   (interactive)
  1149.   (setq ksh-completion-list
  1150.     (list
  1151.      (cons "if"  ksh-completion-type-misc)
  1152.      (cons "while"  ksh-completion-type-misc)
  1153.      (cons "until"  ksh-completion-type-misc)
  1154.      (cons "select"  ksh-completion-type-misc)
  1155.      (cons "for"  ksh-completion-type-misc)
  1156.      (cons "continue"  ksh-completion-type-misc)
  1157.      (cons "function"  ksh-completion-type-misc)
  1158.      (cons "fi"  ksh-completion-type-misc)
  1159.      (cons "case"  ksh-completion-type-misc)
  1160.      (cons "esac"  ksh-completion-type-misc)
  1161.      (cons "break"  ksh-completion-type-misc)
  1162.      (cons "exit"  ksh-completion-type-misc)
  1163.      (cons "done"  ksh-completion-type-misc)
  1164.      (cons "do"  ksh-completion-type-misc))))
  1165.  
  1166. (defun ksh-eol-point ()
  1167.   (save-excursion
  1168.     (end-of-line)
  1169.     (point)))
  1170.  
  1171. (defun ksh-bol-point ()
  1172.   (save-excursion
  1173.     (beginning-of-line)
  1174.     (point)))
  1175.  
  1176. (defun ksh-pickup-all ()
  1177.   "Pickup all completions in buffer."
  1178.   (interactive)
  1179.   (ksh-pickup-completion-driver (point-min) (point-max) t))
  1180.  
  1181. (defun ksh-pickup-this-line ()
  1182.   "Pickup all completions in current line."
  1183.   (interactive)
  1184.   (ksh-pickup-completion-driver (ksh-bol-point) (ksh-eol-point) nil))
  1185.  
  1186. (defun ksh-pickup-completion-driver (pmin pmax message)
  1187.   "Driver routine for ksh-pickup-completion."
  1188.   (if message
  1189.       (message "pickup completion..."))
  1190.   (let* (
  1191.      (i1
  1192.       (ksh-pickup-completion  ksh-completion-regexp-var
  1193.                  ksh-completion-type-var
  1194.                  ksh-completion-match-var
  1195.                  pmin pmax))
  1196.      (i2
  1197.       (ksh-pickup-completion  ksh-completion-regexp-var2
  1198.                  ksh-completion-type-var
  1199.                  ksh-completion-match-var2
  1200.                  pmin pmax))
  1201.      (i3
  1202.       (ksh-pickup-completion  ksh-completion-regexp-function
  1203.                  ksh-completion-type-function
  1204.                  ksh-completion-match-function
  1205.                  pmin pmax)))
  1206.     (if message
  1207.     (message "pickup %d variables and %d functions." (+ i1 i2) i3))))
  1208.  
  1209. (defun ksh-pickup-completion (regexp type match pmin pmax)
  1210.   "Pickup completion in region and addit to the list, if not already
  1211. there." 
  1212.   (let ((i 0) kw obj)
  1213.     (save-excursion
  1214.       (goto-char pmin)
  1215.       (while (and
  1216.           (re-search-forward regexp pmax t)
  1217.           (match-beginning match)
  1218.           (setq kw  (buffer-substring
  1219.              (match-beginning match)
  1220.              (match-end match))))
  1221.     (progn
  1222.       (setq obj (assoc kw ksh-completion-list))
  1223.       (if (or (equal nil obj)
  1224.           (and (not (equal nil obj))
  1225.                (not (= type (cdr obj)))))
  1226.           (progn
  1227.         (setq i (1+ i))
  1228.         (ksh-addto-alist kw type))))))
  1229.     i))
  1230.  
  1231. (defun ksh-complete-symbol ()
  1232.   "Perform completion."
  1233.   (interactive)
  1234.   (let* ((case-fold-search)
  1235.      (end (point))
  1236.          (beg (unwind-protect
  1237.                   (save-excursion
  1238.                     (backward-sexp 1)
  1239.                     (while (= (char-syntax (following-char)) ?\')
  1240.                       (forward-char 1))
  1241.                     (point))))
  1242.          (pattern (buffer-substring beg end))
  1243.      (predicate 
  1244.       ;;
  1245.       ;; ` or $( mark a function
  1246.       ;;
  1247.       (save-excursion
  1248.         (goto-char beg)
  1249.         (if (or
  1250.          (save-excursion
  1251.            (backward-char 1)
  1252.            (looking-at "`"))
  1253.          (save-excursion
  1254.            (backward-char 2)
  1255.            (looking-at "\\$(")))
  1256.         (function (lambda (sym)
  1257.                 (equal (cdr sym) ksh-completion-type-function)))
  1258.           ;;
  1259.           ;; a $, ${ or ${# mark a variable
  1260.           ;;
  1261.           (if (or
  1262.            (save-excursion
  1263.              (backward-char 1)
  1264.              (looking-at "\\$"))
  1265.            (save-excursion
  1266.              (backward-char 2)
  1267.              (looking-at "\\${"))
  1268.            (save-excursion
  1269.              (backward-char 3)
  1270.              (looking-at "\\${#")))
  1271.           (function (lambda (sym)
  1272.                   (equal (cdr sym)
  1273.                      ksh-completion-type-var)))
  1274.         ;;
  1275.         ;; don't know. use 'em all
  1276.         ;;
  1277.         (function (lambda (sym) t))))))
  1278.      ;;
  1279.      (completion (try-completion pattern ksh-completion-list predicate)))
  1280.     ;;
  1281.     (cond ((eq completion t))
  1282.       ;;
  1283.       ;; oops, what is this ?
  1284.       ;;
  1285.           ((null completion)
  1286.            (message "Can't find completion for \"%s\"" pattern))
  1287.       ;;
  1288.       ;; insert
  1289.       ;;
  1290.           ((not (string= pattern completion))
  1291.            (delete-region beg end)
  1292.            (insert completion))
  1293.       ;;
  1294.       ;; write possible completion in the minibuffer,
  1295.       ;; use this instead of a separate buffer (usual)
  1296.       ;;
  1297.           (t
  1298.            (let ((list (all-completions pattern ksh-completion-list predicate))
  1299.          (string ""))
  1300.          (while list
  1301.            (progn
  1302.          (setq string (concat string (format "%s " (car list))))
  1303.          (setq list (cdr list))))
  1304.          (message string))))))
  1305.  
  1306. (provide 'ksh-mode)
  1307. ;;; ksh-mode.el ends here
  1308.